/**
 * 
 * 单例模式
 */
function Person(name) {
    this.name = name;
}
Person.prototype.sayName = function () {
    console.log(this.name);
}

let factory = (function () {
    let instance = null;
    return function (name) {
        if (instance === null) {
            instance = new Person(name);
        }
        return instance;
    }
})();

let p1 = factory("hh");
let p2 = factory("hello")
p2.sayName();

/**
 * 防抖实现，在计时器n秒内又触发了这个事件，此时计时器清零
 */

function debounce(fn, timeout) {
    let timer;
    return function (...args) {
        let context = this;
        clearTimeout(timer);
        timeout = setTimeout(function () {
            fn.apply(context, args);
        }, timeout)
    }
}
/**
 * 节流实现
 */
function throttle(fn, wait) {
    let prev_time = null;
    let self = this;
    return function (...args) {
        let context = this;
        let now_time = +new Date();
        if (now_time - prev_time >= wait || !prev_time) {
            console.log(context === self);
            fn.apply(self, args);
            prev_time = now_time;
        }
    }
}
//  function func(){
//      console.log(1);
//  }
//  setInterval(throttle(func,5000),10);

/**
 * 封装判断类型
 */
function getType(val) {
    if (val === null) {
        return val + '';
    } else if (typeof val === 'object') {
        let str = Object.prototype.toString.call(val).split(' ')[1].split('');
        str.pop();
        return str.join('').toLowerCase();
    } else {
        return typeof val;
    }
}

//  let obj = null;
//  console.log(getType(obj))

/**
 * 函数柯里化
 */
function curry(fn, ...args) {
    let len = fn.length;
    args = args || [];
    return function () {
        let all = args.slice(0);
        all = all.concat([...arguments].slice(0));
        console.log(all);
        if (all.length >= len) {
            return fn.apply(this, all);
        } else {
            return curry.call(this, fn, all);
        }
    }
}

function add(x, y) {
    // console.log(x+y)
    return (+x) + (+y);
}
let addcurry = curry(add);
console.log(addcurry(1)(2));


/**
 * 函数扁平化处理
 */
function flat(arr) {
    let len = arr.length;
    let ans = [];
    for (let i = 0; i < len; i++) {
        if (Array.isArray(arr[i])) {
            ans = ans.concat(flat(arr[i]));
        } else {
            ans.push(arr[i]);
        }
    }
    return ans;
}

//利用栈的特性
function flat1(arr) {
    let stack = [].concat(arr);
    let ans = [];
    while (stack.length > 0) {
        let item = stack.pop();//最后一个元素
        if (Array.isArray(item)) {
            stack.push(...item);
        } else {
            ans.unshift(item);
        }
    }
    return ans;
}

/**
 * 手写jsonp
 */
function jsonp(url, params, callback) {
    let queryString = url.indexOf('?') === -1 ? '?' : '&';
    for (let x in params) {
        if (params.hasOwnProperty(x)) {
            queryString += k + "=" + params[k] + '&';
        }
    }
    let random = Math.random().toString().replace('.', '');
    let callbackName = "callback" + random;
    queryString += callback + "=" + callbackName;

    window[callbackName] = function (...args) {
        callback(args);
        document.getElementsByTagName('head')[0].removeChild(script);
    }

    let script = document.createElement('script');
    script.src = url + queryString;
    document.getElementsByTagName('head')[0].appendChild(script);
}


/**
 * 模拟new过程
 */
function fakeNew(...args) {
    let obj = {};
    let Constructor = args[0]; //构造函数
    args.shift(); //后面就是构造参数
    Object.setPrototypeOf(obj, Constructor.prototype);
    let res = Constructor.apply(obj, args);
    console.log(res, obj);
    return typeof res === 'object' ? res : obj
}

let p = fakeNew(Person, "hello");
p.sayName();

/**
 * 手写trim过程
 */
function fakeTrim(str) {
    return str.replace(/^\s+|\s+&/g, '');

}
let str = " hello  ";
fakeTrim(str);
// console.log(str);


/**
 * 深浅拷贝
 */
//浅拷贝
function shallowCopy(obj) {
    if (typeof obj !== 'object') {
        throw new Error("TypeError");
    }
    let newobj = Array.isArray(obj) ? [] : {};
    Reflect.ownKeys(obj).forEach(key => {
        newobj[key] = obj[key];
    })
    return newobj;
}

//深拷贝
function deepCopy(obj) {
    function isObject(val) {
        return (typeof val === 'object' || typeof val === 'function') && val !== null;
    }
    if (!isObject(obj)) {
        throw new Error("TypeError");
    }
    let newobj = Array.isArray(obj) ? [] : {};
    Reflect.ownKeys(obj).forEach(key => {
        newobj[key] = isObject(obj[key]) ? deepCopy(obj[key]) : obj[key];
    })
    return newobj;
}

/**
 * 手写观察者模式
 */
class Watcher {
    constructor() {
        this.events = {};
    }
    subscribe(eventName, handler) {
        if (!this.events[eventName]) {
            this.events[eventName] = [handler];
        } else {
            this.events[eventName].push(handler);
        }
        return this;
    }
    publish(eventName, ...args) {
        if (!this.events[eventName]) {
            return;
        }
        this.events[eventName].forEach(func => {
            func(...agrs);
        })
        return this;
    }
    remove(eventName, handler) {
        if (!this.events[eventName]) {
            return this;
        }
        let func = this.events[eventName].filter(item => {
            return item !== handler;
        })
        this.events[eventName] = func;
        return this;
    }
    removeAll(eventName) {
        if (!this.events[eventName]) return this;
        this.events[eventName] = null;
        return this;
    }
}

/**
 * 手写一个eventemmiter
 */
class EventEmmitter {
    constructor() {
        this.events = {};
    }
    on(eventName, handler) {
        if (!this.events[eventName]) {
            this.events[eventName] = [];
        }
        this.events[eventName].push(handler);
    }
    emit(eventName, ...args) {
        if (!this.events[eventName]) {
            return;
        }
        this.events[eventName].forEach(func => {
            func(args);
        })
        return this;
    }
    remove(eventName, handler) {
        if (!this.events[eventName]) {
            return;
        }
        this.events[eventName] = this.events[eventName].filter(func => {
            return func !== handler;
        })
    }
    once(eventName, handler) {
        let func = function () {
            handler();
            this.remove(eventName, handler);
        }
        this.on(eventName, func);
    }
}


/**
 * 模拟Promise实现
 */
function MyPromise(fn) {
    let self = this;
    this.status = 'pending';
    this.val = undefined;
    this.err = undefined;

    this.resolveCallbacks = [];
    this.rejectCallbacks = [];

    function resolve(val) {
        setTimeout(() => {
            if (this.status === 'pending') {
                this.stack = "resolved";
                this.val = val;
                this.resolveCallbacks.forEach(func => {
                    func(val);
                })
            }
        }, 0)

    }

    function reject(err) {
        setTimeout(() => {
            if (this.status === 'pending') {
                this.status = 'rejectd';
                this.err = err;
                this.rejectCallbacks.forEach(func => {
                    func(err);
                })
            }
        }, 0)
    }
    try{
        fn(resolve, reject);
    } catch (e) {
        reject(e)
    }
}
MyPromise.prototype.then = function (resolve, reject) {
    if (this.status === 'resolved') {
        resolve(this.val);
    } else if (this.status === 'rejectd') {
        reject(this.err);
    } else {
        this.resolveCallbacks.push(resolve);
        this.rejectCallbacks.push(reject);
    }
}


let pro = new MyPromise(function(resolve,reject){
    //模拟异步调用
    console.log(111);
    setTimeout(() => {
        resolve("转成resolved");    
    }, 1000);
})
pro.then(data=>{
    console.log(data);
},err=>{
    console.log(err);
});

/**
 * 函数防抖
 */
function debounce(fn,wait){
    let timer = null;
    return function(...args){
        let context = this;
        if(timer){
            clearTimeout(timer);
            timer = null;
        }
        timer = setTimeout(()=>{
            fn.apply(context,args);
        },wait)
    }
}
/**
 * 函数节流
 */
function throttle(fn,wait){
    let prev = Date.now();
    return function(...args){
        let context = this;
        let nowtime = Date.now();
        if(nowtime - prev >= wait){
            prev = Date.now();
            return fn.apply(context,args)
        }
    }
}